package com.zx._01_基础._011_集合;

import java.lang.reflect.Field;
import java.util.HashMap;
import org.springframework.util.ReflectionUtils;

/**
 * 使用不同的参数，对扩容过程进行对比
 */
public class HashMapTest2 {

    private static final int len = 100;
    private static final Field thresholdField;    // 加载的阈值
    private static final Field tableField;        // 数据
    private static final Field loadFactorField; // 加载因子

    static {
        // HashMap中需要访问的字段
        thresholdField = ReflectionUtils.findField(HashMap.class, "threshold");
        tableField = ReflectionUtils.findField(HashMap.class, "table");
        loadFactorField = ReflectionUtils.findField(HashMap.class, "loadFactor");
        //final private 可以进行修改操作   注意： final是可以通过反射被修改的： jdk1.8
        thresholdField.setAccessible(true);
        tableField.setAccessible(true);
        loadFactorField.setAccessible(true);
    }

    public static void main(String[] args) throws Exception {
//		test1();
//		test2();
//		test3();
        test4();
    }

    // 1、默认的容量和加载因子的变化
    public static void test1() throws Exception {
        HashMap<String, String> map = new HashMap<String, String>();

        int threshold = (int) thresholdField.get(map);
        System.out.println("初始化容量=" + threshold);

        int count = 0;//扩容次数，第一次初始化到16不算
        for (int i = -1; i < len; i++) {
            map.put("" + i, "" + i);
            int tt = (int) thresholdField.get(map);
            if (tt != threshold) {
                threshold = tt;
                Object[] os = (Object[]) tableField.get(map);
                System.out.println(
                        "size(数据量)=" + (i + 1) + ", threshold(扩容阈值)=" + threshold + ", tableLen(真实容量)=" + os.length);
                count++;
            }
        }
        System.out.println("数据量=，" + len + "扩容次数=" + count);
    }

    // 2、设置初始化容量
    public static void test2() throws Exception {
        // 正确的设置初始化值 元素数量 / 负载因子 + 1，这个值只是为了更好的避免扩展，不是真实的容量
        int cap = (int) (len / 0.75) + 1;
        System.out.println("设置容量 cap=" + cap);// 比如  元素=100， cap=134, 初始化容量=256,threshold=192。
        // 我们发现threshold 设置时是256，第一次保存数据的时候，变成了192。这是因为第一次初始化的时候，重新计算的阈值。最开始的256是真实的容量。
        HashMap<String, String> map = new HashMap<String, String>(cap);

        int threshold = (int) thresholdField.get(map);
        System.out.println("初始化容量=" + threshold);

        int count = -1;//扩容次数，第一次初始化到16不算
        for (int i = 0; i < len; i++) {
            map.put("" + i, "" + i);
            int tt = (int) thresholdField.get(map);
            if (tt != threshold) {
                threshold = tt;
                Object[] os = (Object[]) tableField.get(map);
                System.out.println(
                        "size(数据量)=" + (i + 1) + ", threshold(扩容阈值)=" + threshold + ", tableLen(真实容量)=" + os.length);
                count++;
            }
        }
        System.out.println("数据量=" + len + ", 扩容次数=" + count);
    }

    // 3、设置加载因子，默认0.75，但是>=1的话，会被修复为默认值
    public static void test3() throws Exception {
        HashMap<String, String> map = new HashMap<String, String>(0, 2);
        int threshold = (int) thresholdField.get(map);
        System.out.println("初始化容量=" + threshold);

        int count = -1;//扩容次数，第一次初始化到16不算
        for (int i = 0; i < len; i++) {
            map.put("" + i, "" + i);
            int tt = (int) thresholdField.get(map);
            if (tt != threshold) {
                threshold = tt;
                Object[] os = (Object[]) tableField.get(map);
                System.out.println(
                        "size(数据量)=" + (i + 1) + ", threshold(扩容阈值)=" + threshold + ", tableLen(真实容量)=" + os.length);
                count++;
            }
        }
        System.out.println("数据量=" + len + ", 扩容次数=" + count);
    }

    // 4.加载因子大于1的时候，内部数组虽然扩容变得不频繁了，但是hash碰撞的次数变的非常多，几乎数组的每个位置都会生成链表，极大的影响性能
    public static void test4() throws Exception {
        HashMap<String, String> map = new HashMap<String, String>(16, 2F);
        for (int i = 0; i < len; i++) {
            map.put("" + i, "" + i);
            int tt = (int) thresholdField.get(map);// 阈值
            Object[] os = (Object[]) tableField.get(map);//数据
            System.out.println("size(数据量)=" + (i + 1) + ", threshold(扩容阈值)=" + tt + ", tableLen(真实容量)=" + os.length);
        }
    }
}
